﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Sgr.AspNetCore;
using Sgr.Oss.Services;
using Sgr.Oss.ViewModels;
using Sgr.Utilities;

namespace Sgr.Oss.Apis
{
    /// <summary>
    /// 对象存储 Web API 终结点
    /// </summary>
    public static class OssEndpoints
    {
        /// <summary>
        /// 添加对象存储 Web API 终结点
        /// </summary>
        public static IEndpointRouteBuilder MapOssEndpoints(this IEndpointRouteBuilder endpoints)
        {
            var group = endpoints.MapGroup("api/v1/sgr/oss/local");

            group.WithTags("OssLocal");

            // 获取对象内容
            group.MapGet("/{bucket}/{*objectPath}", async (
                 [FromRoute] string bucket,
                 [FromRoute] string objectPath,
                 [FromQuery] string sig,
                 [FromQuery] string exp,
                HttpContext context,
                IOssService ossService,
                IOptions<LocalFilesystemOptions> options,
                CancellationToken cancellationToken) =>
            {
                try
                {
                    // 验证过期时间
                    if (!long.TryParse(exp, out var expireTime) ||
                        DateTimeHelper.TimestampSecondsToDateTimeOffset(expireTime) < DateTimeOffset.UtcNow)
                    {
                        return ResultsExtensions.ExpiredUrl(context);
                    }

                    // 验证签名
                    var expectedSignature = LocalFilesystemOssService.GenerateGetSignature(bucket, objectPath, exp, options.Value.SignKey);
                    if (!string.Equals(sig, expectedSignature, StringComparison.OrdinalIgnoreCase))
                    {
                        return ResultsExtensions.InvalidSignature(context);
                    }

                    // 检查对象是否存在
                    if (!await ossService.ObjectsAnyAsync(bucket, objectPath, cancellationToken))
                    {
                        return ResultsExtensions.ObjectNotFound(context);
                    }

                    // 获取对象元数据
                    var metadata = await ossService.GetObjectMetadataAsync(bucket, objectPath, cancellationToken);

                    // 设置响应头
                    context.Response.ContentType = metadata.ContentType;
                    context.Response.Headers.ContentDisposition = $"inline; filename=\"{Path.GetFileName(objectPath)}\"";
                    context.Response.Headers.ETag = metadata.ETag;
                    context.Response.Headers.LastModified = metadata.LastModified.ToString("R");

                    // 流式返回文件内容
                    return Results.Stream(async stream =>
                    {
                        await ossService.GetObjectAsync(bucket, objectPath,
                            async content => await content.CopyToAsync(stream),
                            cancellationToken: cancellationToken);
                    });
                }
                catch (Exception ex)
                {
                    return Results.Problem(ex.Message);
                }
            })
                .WithName("GetOss")
                .WithSummary("获取本地Oss对象")
                .WithDescription("返回本地Oss对象")
                .RequirePermission(Permissions.QueryOss) // 需要查询权限
                .Produces<FileContentResult>(StatusCodes.Status200OK); // 指定返回类型

            // 上传对象内容
            group.MapPut("/{bucket}/{*objectPath}", async (
                [FromRoute] string bucket,
                [FromRoute] string objectPath,
                [FromQuery] string sig,
                [FromQuery] string exp,
                [FromQuery] string method,
                [FromQuery(Name = "content-type")] string? contentType,
                HttpContext context,
                IOssService ossService,
                IOptions<LocalFilesystemOptions> options,
                CancellationToken cancellationToken) =>
            {
                try
                {
                    // 1. 验证基本参数
                    if (string.IsNullOrEmpty(method) || !method.Equals("PUT", StringComparison.CurrentCultureIgnoreCase))
                    {
                        return Results.Problem(
                            statusCode: StatusCodes.Status400BadRequest,
                            title: "invalid-method",
                            type: ProblemTypes.Validation,
                            detail: "无效的请求方法",
                            instance: context.Request.Path);
                    }

                    // 2. 验证过期时间
                    if (!long.TryParse(exp, out var expireTime) ||
                        DateTimeHelper.TimestampSecondsToDateTimeOffset(expireTime) < DateTimeOffset.UtcNow)
                    {
                        return ResultsExtensions.ExpiredUrl(context);
                    }

                    // 3. 收集所有查询参数（按字母顺序）
                    var parameters = new Dictionary<string, string>();
                    foreach (var param in context.Request.Query.OrderBy(q => q.Key))
                    {
                        if (param.Key != "sig") // 排除签名本身
                        {
                            parameters[param.Key] = param.Value.ToString();
                        }
                    }

                    // 4. 构建签名输入
                    var signatureInput = string.Join("/",
                        bucket,
                        objectPath,
                        exp,
                        string.Join("&", parameters.OrderBy(p => p.Key).Select(p => $"{p.Key}={Uri.EscapeDataString(p.Value)}")),
                        options.Value.SignKey);

                    // 5. 验证签名
                    var expectedSignature = HashHelper.CreateSha256(signatureInput).ToLowerInvariant();
                    if (!string.Equals(sig, expectedSignature, StringComparison.OrdinalIgnoreCase))
                    {
                        return ResultsExtensions.InvalidSignature(context);
                    }

                    // 7. 提取元数据
                    var metadata = new Dictionary<string, string>();
                    foreach (var param in parameters)
                    {
                        if (param.Key.StartsWith("x-sgr-meta-"))
                        {
                            metadata[param.Key["x-sgr-meta-".Length..]] = param.Value;
                        }
                    }

                    // 8. 上传对象
                    var stream = context.Request.Body;
                    var success = await ossService.PutObjectAsync(
                        bucket,
                        objectPath,
                        stream,
                        metadata,
                        ObjectAcl.Inherited,
                        cancellationToken: cancellationToken);

                    if (!success)
                    {
                        return Results.BadRequest("上传失败");
                    }

                    // 9. 获取对象元数据作为响应
                    var objectMetadata = await ossService.GetObjectMetadataAsync(
                        bucket,
                        objectPath,
                        cancellationToken);

                    // 10. 返回成功响应
                    var expiry = TimeSpan.FromHours(1);
                    var presignedUrl = await ossService.GetPresignedGetUrlAsync(bucket, objectPath, expiry, cancellationToken);
                    return Results.Ok(new OssUploadModel
                    {
                        Bucket = bucket,
                        Expiry = DateTime.UtcNow.Add(expiry).ToIso8601String(),  // 添加过期时间
                        ObjectPath = objectPath,
                        ETag = objectMetadata.ETag,
                        LastModified = objectMetadata.LastModified,
                        Size = objectMetadata.Size
                    });
                }
                catch (Exception ex)
                {
                    return Results.Problem(ex.Message);
                }
            })
            .WithName("PutOss")
            .WithSummary("上传本地Oss对象")
            .WithDescription("上传对象到本地Oss存储")
            .RequirePermission()
            .Produces<OssUploadModel>(StatusCodes.Status200OK); // 指定返回类型

            return endpoints;
        }
    }
}